package jmm;

import org.junit.Test;

public class HappensBefore {

	private int shareVal = 1;

	private volatile int volatileShareVal = 1;

	private final Object mutex = new Object();

	/**
	 * @Description 管程中，前一个线程的解锁对后一个线程的加锁是可见的。（如果不能保证可见，锁的竞争就会有问题,也无法保证互斥同步）
	 * @author chenwb
	 * @date 2020/11/3 18:45
	 */
	@Test
	public void theLastThreadUnlockAreSeenByNextThreadLock() throws InterruptedException {
		Runnable runnable = () -> {
			synchronized (mutex) {
				shareVal++;
			}
		};
		Thread work1 = new Thread(runnable);
		Thread work2 = new Thread(runnable);
		work1.start();
		work2.start();
		work1.join();
		work2.join();
	}

	/**
	 * @Description volatile变量的写操作对后续的读操作是可见的。（禁用缓存，同时加入内存屏障禁止了指令重排序）
	 * @author chenwb
	 * @date 2020/11/3 18:45
	 */
	@Test
	public void theOperationAfterWitriingVolitaleValCanSeeVolitaleVal() throws InterruptedException {
		Thread child = new Thread(() -> {
			for(;;) {
				System.out.println(volatileShareVal);
				if(volatileShareVal == 77) {
					System.out.println(volatileShareVal);
					break;
				}
			}
		});
		child.start();
		// 此处的写对于后续对该变量的读都是可见的
		volatileShareVal = 77;
		child.join();
	}

	/**
	 * @Description 主线程启动子线程，在子线程start前面的操作对子线程来说都是可见的。（目前的猜想是禁止重排序）
	 * @author chenwb
	 * @date 2020/11/3 18:45
	 */
	@Test
	public void theOperationsBeforeChildThreadStartAreSeenInChildThread() throws InterruptedException {
		Thread child = new Thread(() -> System.out.println(shareVal));
		shareVal = 77;
		// start之前的操作对于子线程是可见的，所以一定打印77
		child.start();
		// 加join是为了让子线程顺利打印，否则可能主线程会比子线程提前退出
		child.join();
	}

	/**
	 * @Description 主线程启动子线程，子线程调用join方法之后，子线程中的操作对join之后的代码都是可见的。（目前的猜想是禁止重排序）
	 * @author chenwb
	 * @date 2020/11/3 18:45
	 */
	@Test
	public void theOperationsBeforeChildThreadJoinAreSeenAfterThreadJoin() throws InterruptedException {
		Thread child = new Thread(() -> shareVal = 77);
		child.start();
		child.join();
		// 子线程中的操作对于这里是可见的，所以结果一定是77
		System.out.println(shareVal);
	}

	/**
	 * @Description 线程中断规则：线程发起中断interrupt对于后续的中断检测代码Thread.interrupted()都是可见的。
	 * @author chenwb
	 * @date 2020/11/3 18:45
	 */
	@Test
	public void theInterruptOperationsAreSeenByThreadInterrupted() throws InterruptedException {
		Thread child = new Thread(() -> {
			// 自旋判断是否中断
			for (;;) {
				if (Thread.interrupted()) {
					// 中断发起之前的操作对于中断检测后是可见的（传递性规则）
					System.out.println(shareVal);
					break;
				}
			}
		});
		child.start();
		shareVal = 77;
		child.interrupt();
		child.join();
	}
}
